/*****************************************************************************
* | File      	:	DEV_Config.cpp
* | Author      : Waveshare team
* | Function    :
* | Info        :
*   Image scanning
*      Please use progressive scanning to generate images or fonts
*----------------
* |	This version:   V1.0
* | Date        :   2018-01-11
* | Info        :   Basic version
*
******************************************************************************/
#include <rthw.h>
#include <rtdef.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>

#include "DEV_Config.h"
#include "board.h"
#include "drivers/pin.h"
#include "drivers/spi.h"
#include "drv_spi.h"
// #include "bsp_spi.h"

struct pin_context {
    const char* label; // for log print
    rt_base_t pin;
};

#define INVLIAD_PIN UINT32_MAX

struct pin_context pin_ctx_table[EPD_DEV_PIN_MAX] = {
#define PIN_MAP(label, pin) [label] = {#label, pin}
        PIN_MAP(EPD_CS_PIN, INVLIAD_PIN),
        PIN_MAP(EPD_DC_PIN, INVLIAD_PIN),
        PIN_MAP(EPD_RST_PIN, INVLIAD_PIN),
        PIN_MAP(EPD_BUSY_PIN, INVLIAD_PIN),
        PIN_MAP(RAM_CS_PIN, INVLIAD_PIN),
        PIN_MAP(SD_CS_PIN, INVLIAD_PIN),
#undef PIN_MAP
};

#define PIN_ID_NUM (sizeof(pin_ctx_table)/sizeof(pin_ctx_table[0]))

void DEV_Digital_Write(uint8_t id, uint8_t value)
{
    if (id > PIN_ID_NUM) {
        rt_kprintf("pin %d invalid!\n", id);
        return;
    }

    rt_base_t pin = pin_ctx_table[id].pin;
    if (pin == INVLIAD_PIN) {
        rt_kprintf("pin %s not set!\n", pin_ctx_table[id].label);
        return;
    }
    rt_pin_write(pin, value ? PIN_HIGH : PIN_LOW);
}

uint8_t DEV_Digital_Read(uint8_t id)
{
    if (id > PIN_ID_NUM) {
        rt_kprintf("pin %d invalid!\n", id);
        return 0xFF;
    }

    rt_base_t pin = pin_ctx_table[id].pin;
    if (pin == INVLIAD_PIN) {
        rt_kprintf("pin %s not set!\n", pin_ctx_table[id].label);
        return 0;
    }
    return rt_pin_read(pin);
}

static void GPIO_PIN_Init(uint8_t id, const char* name, rt_base_t mode)
{
    if (id > PIN_ID_NUM) {
        rt_kprintf("pin %d invalid!\n", id);
        return;
    }

    rt_base_t pin = rt_pin_get(name);
    if (pin < 0) {
        rt_kprintf("rt_pin_get failed, '%s' not found\n", name);
        return;
    }

    pin_ctx_table[id].pin = pin;
    rt_pin_mode(pin, mode);
}

static void DEV_PIN_Init(void)
{
    GPIO_PIN_Init(EPD_CS_PIN, "PB.4", PIN_MODE_OUTPUT); // also used to control Blue LED
    GPIO_PIN_Init(EPD_DC_PIN, "PE.10", PIN_MODE_OUTPUT);
    GPIO_PIN_Init(EPD_RST_PIN, "PE.11", PIN_MODE_OUTPUT);
    GPIO_PIN_Init(EPD_BUSY_PIN, "PE.12", PIN_MODE_INPUT);
    GPIO_PIN_Init(RAM_CS_PIN, "PB.5", PIN_MODE_OUTPUT); // also used to control Green LED
}

#define SRAM_SPI_CS_PORT         GPIOA
#define SRAM_SPI_CS_PIN          GPIO_PIN_8
#define SRAM_SPI_BUS_NAME        "spi2"
#define SRAM_SPI_DEV_NAME        "spi20"

static int SPI_BUS_Attach()
{
    rt_err_t err = rt_hw_spi_device_attach(SRAM_SPI_BUS_NAME, SRAM_SPI_DEV_NAME, SRAM_SPI_CS_PORT, SRAM_SPI_CS_PIN);
    if (err != RT_EOK) {
        rt_kprintf("SPI device %s attach FAIL!\n", SRAM_SPI_DEV_NAME);
        return 1;
    }
    rt_kprintf("SPI device %s attach OK!\n", SRAM_SPI_DEV_NAME);
    return 0;
}

static struct rt_spi_device* spi_dev = RT_NULL;

static void SPI_DEV_Init(void)
{
    SPI_BUS_Attach();
    spi_dev = (struct rt_spi_device*) rt_device_find(SRAM_SPI_DEV_NAME);
    if (spi_dev != RT_NULL) {
        rt_kprintf("find %s OK!\n", SRAM_SPI_DEV_NAME);
    } else {
        rt_kprintf("find %s FAILED!\n", SRAM_SPI_DEV_NAME);
    }

    struct rt_spi_configuration spi_config = {0};
    spi_config.data_width = 8;
    spi_config.max_hz = 1 * 1000 * 1000;
    spi_config.mode = RT_SPI_MASTER | RT_SPI_MODE_0 | RT_SPI_MSB;

    rt_kprintf("configure %s ...\n", SRAM_SPI_DEV_NAME);
    rt_spi_take_bus(spi_dev);
    rt_spi_configure(spi_dev, &spi_config);
    rt_spi_release_bus(spi_dev);
}

/******************************************************************************
function:	Initialize Arduino, Initialize Pins, and SPI
parameter:
Info:
******************************************************************************/
UBYTE DEV_Module_Init(void)
{
    DEV_PIN_Init();
    SPI_DEV_Init();

    EPD_CS_1;
    SPIRAM_CS_1;

    return 0;
}

static int epd_dev_init()
{
    rt_kprintf("DEV_Module_Init ...\n");
    DEV_Module_Init();
    return 0;
}
INIT_ENV_EXPORT(epd_dev_init);

/*********************************************
function:	Hardware interface
note:
	SPI4W_Write_Byte(value) : 
		Register hardware SPI
*********************************************/	
void DEV_SPI_WriteByte(UBYTE value)
{
    struct rt_spi_message msg = {0};
    msg.send_buf = &value;
    msg.length = 1;
//    rt_kprintf("spi_send: 0x%02X\n", value);

    rt_spi_transfer_message(spi_dev, &msg);
}

UBYTE DEV_SPI_ReadByte(void)
{
    UBYTE byte = 0;
    struct rt_spi_message msg = {0};
    msg.recv_buf = &byte;
    msg.length = 1;

    rt_spi_transfer_message(spi_dev, &msg);
    return byte;
}

/******************************************************************************
function:	Analog microsecond delay
parameter:
Info:
******************************************************************************/
void DEV_Delay_us(UWORD xus)
{
    for(int j=xus; j > 0; j--);
}

void DEV_Module_Exit(void)
{
    DEV_Digital_Write(EPD_DC_PIN, 0);
    DEV_Digital_Write(EPD_CS_PIN, 0);

    //close 5V
    DEV_Digital_Write(EPD_RST_PIN, 0);
}
